#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import os
import types
import json
import salt.client
import urllib2

MASTER_ROOT_DIR = '/vagrant/salt/srv/base/push_guide'
MODULE_NAME = 'push_guide'
LINUX_MINION_ROOT_DIR = '/opt/push_guide/pushed_data'
WINDOWS_MINION_ROOT_DIR = 'C:\push_guide\pushed_data'


class Downloader(object):
    def __init__(self, resource, path, timeout=3):
        '''
        resource: A HTTP or FTP Url
        path: where the file should be put, it's a directory on fs

        '''
        self.resource = resource
        self.path = path
        self.timeout = timeout

    def download(self):
        if self.resource.strip() == '':
            raise Exception('resource is empty')

        file_name = self.resource.split('/')[-1]
        if file_name.strip() == '':
            raise Exception('file_name is empty, resource uri is invalid %s' % self.resource)

        if not os.path.exists(self.path):
            try:
                os.makedirs(self.path)
            except Exception as e:
                raise Exception('can not makedir %s, %s' % (self.path, e))

        file_path = '%(dir)s/%(file_name)s' % {'dir': self.path.rstrip('/'), 'file_name': file_name}
        try:
            response = urllib2.urlopen(self.resource, timeout=self.timeout).read()
            with open(file_path, 'wb') as fp:
                fp.write(response)
        except Exception as e:
            raise Exception("download file failed, %s" % e)



def check_succ(target, salt_result):
    if type(target) != types.StringType:
        raise Exception('target should be a string')

    ret = salt_result.get(target, '')
    if type(ret) == types.StringType:
        return False
    elif type(ret) == types.ListType:
        return True
    else:
        raise Exception('unknow salt result')

def succ_return(msg):
    return {'status': True, 'msg': ''}

def fail_return(msg):
    return {'status': False, 'msg': msg}


class Describer(object):
    def __init__(self, desc):
        self.config = desc
        self.global_desc = self.config['desc']['global_desc']
        self.pre_deploy_desc = self.config['desc']['pre_deploy_desc']
        self.deploy_desc = self.config['desc']['deploy_desc']
        self.post_deploy_desc = self.config['desc']['post_deploy_desc']

    def param_check(self):
        pass


class PushGuide(object):
    def __init__(self, job_desc):
        self.local = salt.client.LocalClient()
        self.job_desc = job_desc
        self.desc =  Describer(self.job_desc)
        self.task_id = self.desc.config.get('task_id')

    def os_kernel(self, target):
        result = self.local.cmd(target,'grains.get', ['kernel'])
        return result.get(target, '')

    def download(self, resource):
        dir_path = '%(dir)s/%(task_id)s' % {
                    'dir': MASTER_ROOT_DIR,
                    'task_id': self.task_id,
                    }
        Downloader(resource, dir_path).download()


    def write_desc_json(self):
        try:
            file_path = '%(dir)s/%(task_id)s/desc.json' % {
                            'dir': MASTER_ROOT_DIR,
                            'task_id': self.task_id,
                        }
            with open(file_path, 'wb') as fp:
                fp.write(json.dumps(self.job_desc))

        except Exception as e:
            raise Exception('write desc json file failed %s' % e)


    def write_inline_shell(self):
        for k,v in {'pre_deploy': self.desc.pre_deploy_desc,
                  'deploy': self.desc.deploy_desc,
                  'post_deploy': self.desc.post_deploy_desc}.items():
            shell_type = v.get('inline_shell_type')
            if shell_type == 'bash':
                ext = 'sh'
            elif shell_type == 'python':
                ext = 'py'
            elif shell_type == 'ruby':
                ext = 'rb'
            elif shell_type == 'bat':
                ext = 'bat'
            else:
                raise Exception('invalid shell type %s' % shell_type)

            file_path = '%(dir)s/%(task_id)s/%(name)s.%(ext)s' % {
                        'dir'     : MASTER_ROOT_DIR,
                        'task_id' : self.task_id,
                        'name'    : k + '_inline',
                        'ext'     : ext,
                       }

            inline_shell_content = v.get('inline_shell', '')
            if inline_shell_content != '':
                with open(file_path, 'wb') as fp:
                   fp.write(v.get('inline_shell'))


    def upload(self, target, timeout=3):
        if self.os_kernel(target) == 'Windows':
            fs_root = WINDOWS_MINION_ROOT_DIR
        else:
            fs_root = LINUX_MINION_ROOT_DIR

        src = 'salt://%(module)s/%(task_id)s' % {
                'module': MODULE_NAME,
                'task_id': self.task_id,
                }
        dst = '%(root)s/' % {
                'root': fs_root,
                }

        result = self.local.cmd(target, 'cp.get_dir', [src, dst], timeout=timeout)


    def prepare(self):
        try:
            # Download resource
            resource_update = self.desc.global_desc.get('resource_update_to')
            resource_rollback = self.desc.global_desc.get('resource_rollback_to')

            if self.desc.deploy_desc.get('install_action') != 'yum':
                self.download(resource_update)
                self.download(resource_rollback)

            else: # we do not need to download anything right now when using yum action
                pass


            # write desc info to file, and prepare to sync to minion
            self.write_desc_json()

            # write inline shell content to file, these file will be pushed to minion
            self.write_inline_shell()

        except Exception as e:
            return fail_return('prepare failed %s' % e)


    def update(self, target):
        try:
            self.upload(target)
            return self.deploy(target, 'update')
        except Exception as e:
            return fail_return('update failed %s' % e)


    def rollback(self, target):
        try:
            return self.deploy(target, 'rollback')
        except Exception as e:
            return fail_return('rollback failed %s' % e)

    def deploy(self, target, action):
        '''
        result = {
            'status': x,
            'msg': xxx,
        }
        '''
        return self.local.cmd(target,'deployer.deploy', [action])





def monitor_disable(job_desc):
    pass

def monitor_enable(job_desc):
    pass

def load_balance_disable(job_desc):
    pass

def load_balance_enable(job_desc):
    pass

# -----------------------------------------

def test_download():
    from zip_job_demo import job
    PushGuide(job).prepare()

def test_upload():
    from zip_job_demo import job
    PushGuide(job).upload('minion')

if __name__ == '__main__':
    test_download()
    test_upload()
    #print    os_kernel('win2008')


